package com.itheima.reggie.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.reggie.entity.Category;
import com.itheima.reggie.entity.Dish;
import com.itheima.reggie.entity.DishFlavor;
import com.itheima.reggie.entity.dto.DishDto;
import com.itheima.reggie.entity.dto.PageDto;
import com.itheima.reggie.mapper.DishMapper;
import com.itheima.reggie.service.CategoryService;
import com.itheima.reggie.service.DishFlavorService;
import com.itheima.reggie.service.DishService;
import com.itheima.reggie.web.R;
import com.itheima.reggie.web.exception.BusinessException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 菜品业务层实现类
 *
 * @Author Vsunks.v
 * @Blog blog.sunxiaowei.net/996.mba
 * @Description: 菜品业务层实现类
 */
@Service
@Transactional  // 在本类上开启事务，需要配合@EnableTransactionManagement生效
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {


    @Autowired
    DishFlavorService dishFlavorService;

    @Autowired
    CategoryService categoryService;

    @Autowired
    DishMapper dishMapper;


    /**
     * 新增菜品，包含口味信息。演示抛出编译时异常时事务失效的场景。
     *
     * @param dishDto 包含口味的菜品
     * @return 是否新增成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    // public R saveWithFlavor(DishDto dishDto) throws Exception {
    public R saveWithFlavorException(DishDto dishDto) throws Exception {

        checkNameDuplcate(dishDto);

        // 保存菜品基本信息
        // MP在成功新增一行记录后，会将生成的id设置会实体对象
        save(dishDto);


        // 获取菜品的口味
        List<DishFlavor> flavors = dishDto.getFlavors();


        // 为每一个口味设置所属菜品id
        Long id = dishDto.getId();
        for (DishFlavor flavor : flavors) {
            flavor.setDishId(id);
        }

        if (true) {
            throw new Exception("xxx");
        }

        // 保存菜品的口味
        dishFlavorService.saveBatch(flavors);


        // 组织数据并返回
        return R.success("新增菜品成功", null);


    }

    /**
     * 检查菜品名称是否重复，重复则抛异常
     *
     * @param dishDto 被检查的产品对象
     */
    private void checkNameDuplcate(DishDto dishDto) {
        // 判断菜品名称是否重复
        String name = dishDto.getName();
        LambdaQueryWrapper<Dish> qw = Wrappers.lambdaQuery(Dish.class)
                .eq(StringUtils.isNotBlank(name), Dish::getName, name)
                .ne(dishDto.getId() != null, Dish::getId, dishDto.getId());
        if (this.count(qw) > 0) {
            throw new BusinessException("名称[ {" + name + "} ]重复，请修改后重试！");
        }
    }

    /**
     * 新增菜品，包含口味信息
     *
     * @param dishDto 包含口味的菜品
     * @return 是否新增成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R saveWithFlavor(DishDto dishDto) {

        // 判断菜品名称是否重复
        checkNameDuplcate(dishDto);

        // 保存菜品基本信息
        // MP在成功新增一行记录后，会将生成的id设置会实体对象
        save(dishDto);


        // 获取菜品的口味
        List<DishFlavor> flavors = dishDto.getFlavors();


        // 为每一个口味设置所属菜品id
        Long id = dishDto.getId();
        for (DishFlavor flavor : flavors) {
            flavor.setDishId(id);
        }

        // 保存菜品的口味
        dishFlavorService.saveBatch(flavors);


        // 组织数据并返回
        return R.success("新增菜品成功", null);


    }

    /**
     * 按条件分页查询菜品(含菜品所属分类名称)
     *
     * @param pageDto 分页条件和查询条件
     * @return 符合要求的包含分类名称的菜品们
     */
    @Override
    public R<Page<DishDto>> pageWithCategoryName(PageDto pageDto) {
        // TODO 参数校验
        Integer pageNum = pageDto.getPage();
        Integer pageSize = pageDto.getPageSize();

        // 构建分页对象
        if (pageNum == null) {
            pageNum = 1;
        }
        if (pageSize == null) {
            pageSize = 10;
        }

        Page<Dish> dishPage = new Page<>(pageNum, pageSize);


        // 构建查询条件对象
        LambdaQueryWrapper<Dish> qw = Wrappers.lambdaQuery(Dish.class)
                .like(StringUtils.isNotBlank(pageDto.getName()), Dish::getName, pageDto.getName())
                .orderByAsc(Dish::getSort)
                .orderByDesc(Dish::getUpdateTime);


        // 分页查询
        page(dishPage, qw);


        if (ObjectUtils.isEmpty(dishPage.getRecords())) {
            return R.fail("查无数据");
        }

        // 组织数据  dishPage-> dishDtoPage  ( page<Dish> + categoryName =   page<DishDto>)
        Page<DishDto> dishDtoPage = new Page<>();
        // 复制分页的基本数据，不复制页面内容（要查询的10条记录）
        BeanUtils.copyProperties(dishPage, dishDtoPage, "records");


        // 查询所有菜品分类
        Category c = new Category();
        c.setType(1);
        R<List<Category>> categoryR = categoryService.listByCondition(c);
        List<Category> categories = categoryR.getData();


        // 手动重新组装页面显示内容（10条记录）并设置进dishDtoPage的records中
        // 根据菜品中分类id，匹配到对应的分类的名称，设置进菜品Dto
        List<DishDto> dishDtoRecords = dishPage.getRecords().stream()
                .map(dish -> {
                    // 创建dishDto用于封装数据
                    DishDto dishDto = new DishDto();

                    // 复制基本数据从dish - > dishDto
                    BeanUtils.copyProperties(dish, dishDto);

                    // 获取分类id
                    Long categoryId = dish.getCategoryId();

                    // 根据分类id查询分类名称
                    // Stream方式
                    /* String categoryName = categories.stream()
                            .filter(category -> category.getId().equals(categoryId))
                            .map(category -> category.getName())
                            .findFirst().get(); */

                    // 增强for方式
                    String categoryName = "";
                    for (Category category : categories) {
                        if (category.getId().equals(categoryId)) {
                            categoryName = category.getName();
                            break;
                        }
                    }

                    // 分类名称设置进dishDto
                    dishDto.setCategoryName(categoryName);

                    // 返回dishDto
                    return dishDto;
                }).collect(Collectors.toList());
        dishDtoPage.setRecords(dishDtoRecords);


        // 并返回
        return R.success(dishDtoPage);
    }

    /**
     * 根据id查询菜品（包含口味）
     *
     * @param id id
     * @return 要查询的菜品
     */
    @Override
    public R<DishDto> getByIdWithFlavors(Long id) {
        // 根据id查询菜品基本信息
        Dish dish = getById(id);

        if (ObjectUtils.isEmpty(dish)) {
            return R.fail("查无此菜！");
        }

        // 根据菜品id查询口味信息
        List<DishFlavor> dishFlavors = dishFlavorService
                .list(Wrappers.lambdaQuery(DishFlavor.class).eq(DishFlavor::getDishId, id));

        // 准备dishDto对象
        DishDto dishDto = new DishDto();

        // 复制属性并设置口味
        BeanUtils.copyProperties(dish, dishDto);
        dishDto.setFlavors(dishFlavors);

        // 返回
        return R.success("查询菜品成功！", dishDto);
    }

    /**
     * 保存修改菜品，包含口味信息
     *
     * @param dishDto 菜品对象(含口味)
     * @return 是否修改成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateByIdWithFlavors(DishDto dishDto) {

        // 判断菜品名称是否重复
        checkNameDuplcate(dishDto);

        // 更新菜品基本信息
        if (!updateById(dishDto)) {
            throw new BusinessException("修改菜品失败！");
        }

        // 更新口味信息
        // 先根据菜品id删除所有口味信息
        Long id = dishDto.getId();
        dishFlavorService.remove(Wrappers.lambdaQuery(DishFlavor.class)
                .eq(DishFlavor::getDishId, id));


        // 保存新的口味信息，部分口味信息中可能没有包含所属菜品的id，需要手动设置
        List<DishFlavor> flavors = dishDto.getFlavors();
        for (DishFlavor flavor : flavors) {
            flavor.setDishId(id);
        }
        dishFlavorService.saveBatch(flavors);

        // 组织数据并返回
        return R.success("修改菜品成功！");
    }

    /**
     * 批量起售停售
     *
     * @param status 目标状态
     * @param ids    目标id们
     * @return 是否成功
     */
    @Override
    public R switchStatus(Integer status, Long[] ids) {

        // 根据id修改状态
        int count = dishMapper.updateStatus(status, ids);
        if (count > 0) {
            return R.success("启停售成功");
        }
        return R.fail("启停售失败");
    }

    /**
     * 根据分类id，查询该分类下所有菜品
     *
     * @param categoryId 分类id
     * @return 符合要求的所有菜品的集合
     */
    @Override
    public R<List<Dish>> listByCondition(Long categoryId) {
        // 构建查询条件（添加隐含条件）
        // 查询并返回
        List<Dish> dishes = list(Wrappers.lambdaQuery(Dish.class).eq(Dish::getCategoryId, categoryId).orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime));

        return R.success(dishes);
    }

    /**
     * 根据条件，查询符合要求的菜品（含口味）
     *
     * @param dish 查询条件
     * @return 符合要求的所有菜品（含口味）的集合
     */
    @Override
    public R<List<DishDto>> listByCondition(Dish dish) {
        // 构建查询条件（添加隐含条件）
        // 查询菜品基本信息
        Long categoryId = dish.getCategoryId();
        String name = dish.getName();
        String desc = dish.getDescription();
        String code = dish.getCode();
        Integer status = dish.getStatus();
        LambdaQueryWrapper<Dish> qw = Wrappers.lambdaQuery(Dish.class)
                .eq(categoryId != null, Dish::getCategoryId, categoryId)
                .eq(status != null, Dish::getStatus, status)
                .like(StringUtils.isNotBlank(name), Dish::getName, name)
                .like(StringUtils.isNotBlank(desc), Dish::getDescription, desc)
                .like(StringUtils.isNotBlank(code), Dish::getCode, code)
                .orderByAsc(Dish::getSort)
                .orderByDesc(Dish::getUpdateTime);
        List<Dish> dishes = list(qw);


        // 查询菜品关联的口味
        // 方式1：查询所有口味
        // List<DishFlavor> dishFlavors = dishFlavorService.list();

        // 方式2：查询dishes相关的所有口味
        // 获取已经dishes中所有菜品id
        List<Long> dishIds = dishes.stream()
                .map(newDish -> newDish.getId())
                .collect(Collectors.toList());

        // 构建查询条件，根据id集合查询口味，得到有效的口味集合
        LambdaQueryWrapper<DishFlavor> dfqw = Wrappers.lambdaQuery(DishFlavor.class)
                .in(!ObjectUtils.isEmpty(dishIds), DishFlavor::getDishId, dishIds);
        // 查询
        List<DishFlavor> dishFlavors = dishFlavorService.list(dfqw);

        // 遍历菜品，得到每一个菜品的id，和口味集合中的dishId挨个匹配。
        List<DishDto> dishDtos = dishes.stream().map(newDish -> {
            // 创建dto对象
            DishDto dishDto = new DishDto();

            // 复制基本数据
            BeanUtils.copyProperties(newDish, dishDto);

            // 把属于某个菜品的口味全部添加到对应的菜品中
            // 获取该菜品的口味信息
            Long newDishId = newDish.getId();
            List<DishFlavor> newDishFlavors = dishFlavors.stream()
                    .filter(dishFlavor -> dishFlavor.getDishId().equals(newDishId))
                    .collect(Collectors.toList());

            // 设置菜品的口味到dto对象
            dishDto.setFlavors(newDishFlavors);

            return dishDto;
            // 组织数据并返回

        }).collect(Collectors.toList());


        return R.success(dishDtos);
    }


    /**
     * 按条件分页查询菜品(含菜品所属分类名称)
     *
     * @param pageDto 分页条件和查询条件
     * @return 符合要求的包含分类名称的菜品们
     */
    public R<Page<DishDto>> pageWithCategoryName2(PageDto pageDto) {
        // TODO 参数校验
        Integer pageNum = pageDto.getPage();
        Integer pageSize = pageDto.getPageSize();

        // 构建分页对象
        if (pageNum == null) {
            pageNum = 1;
        }
        if (pageSize == null) {
            pageSize = 10;
        }

        Page<Dish> dishPage = new Page<>(pageNum, pageSize);


        // 构建查询条件对象
        LambdaQueryWrapper<Dish> qw = Wrappers.lambdaQuery(Dish.class)
                .like(StringUtils.isNotBlank(pageDto.getName()), Dish::getName, pageDto.getName());


        // 分页查询
        page(dishPage, qw);


        if (ObjectUtils.isEmpty(dishPage.getRecords())) {
            return R.fail("查无数据");
        }

        // 组织数据  dishPage-> dishDtoPage  ( page<Dish> + categoryName =   page<DishDto>)
        Page<DishDto> dishDtoPage = new Page<>();
        // 复制分页的基本数据，不复制页面内容（要查询的10条记录）
        BeanUtils.copyProperties(dishPage, dishDtoPage, "records");


        // 手动重新组装页面显示内容（10条记录）并设置进dishDtoPage的records中
        // 根据菜品中分类id，匹配到对应的分类的名称，设置进菜品Dto
        List<DishDto> dishDtoRecords = dishPage.getRecords().stream()
                .map(dish -> {
                    // 创建dishDto用于封装数据
                    DishDto dishDto = new DishDto();

                    // 复制基本数据从dish - > dishDto
                    BeanUtils.copyProperties(dish, dishDto);

                    // 获取分类id
                    Long categoryId = dish.getCategoryId();

                    // 根据分类id查询分类名称
                    Category category = categoryService.getById(categoryId);

                    // 分类名称设置进dishDto
                    dishDto.setCategoryName(category.getName());

                    // 返回dishDto
                    return dishDto;
                }).collect(Collectors.toList());
        dishDtoPage.setRecords(dishDtoRecords);


        // 并返回
        return R.success(dishDtoPage);
    }


}
